#include "CCanvas.h"

#define DRAW_HEIGHT

int shift = 1;

using namespace std;

//-----------------------------------------------------------------------------

void CCanvas::initializeGL() {
    setFocusPolicy(Qt::ClickFocus);

    glClearColor(1.0f, 1.0f, 1.0f, 0.5f); // White Background
    glClearDepth(1.0f); // Depth Buffer Setup
    glEnable(GL_DEPTH_TEST); // Enables Depth Testing
    glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations

    glEnable(GL_MULTISAMPLE); // not needed
    //glDisable(GL_MULTISAMPLE);

    _textureId = loadTexture("../../textures/checkered.bmp");
	_gameovertextureId = loadTexture("../../textures/game-over.jpg");


};

void CCanvas::changeScore(int s) {
    if (_n->checkOverflow(s)) {
        int tmp = s;
        int count = 0;
        while (tmp > 0) {
            tmp /= 10;
            count++;
        }
        _n->setDigitCount(count);
    }

    _n->display(s);
}

//-----------------------------------------------------------------------------

void CCanvas::resizeGL(int width, int height) {
    // set up the window-to-viewport transformation
    glViewport(0, 0, width, height);

    // vertical camera opening angle
    double beta = 90.0;

    // aspect ratio
    double gamma;
    if (height > 0)
        gamma = width / (double) height;
    else
        gamma = width;

    // front and back clipping plane at
    double n = -1.0;
    double f = -50.0;

    // set projection matrix
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(beta, gamma, -n, -f);

};

GLuint CCanvas::loadTexture(const char* imgPath) {

    QImage img(imgPath);
    img = QGLWidget::convertToGLFormat(img);

    //Image* image = loadBMP(imgPath);

    GLuint textureId;
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
    //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->width, image->height , 0, GL_RGB, GL_UNSIGNED_BYTE, image->pixels);

    //delete image;

    return textureId;

}


//-----------------------------------------------------------------------------

void CCanvas::drawCube(Colors c, bool ghost) {

    float alpha = 1.0;
    if (ghost) {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        alpha = 0.4;
    }

    switch (c) {
        case RED:
            glColor4f(1.0, 0.0, 0.0, alpha);
            break;
        case BLUE:
            glColor4f(0.0, 0.0, 1.0, alpha);
            break;
        case GREEN:
            glColor4f(0.0, 1.0, 0.0, alpha);
            break;
        case YELLOW:
            glColor4f(1.0, 1.0, 0.0, alpha);
            break;
        case PURPLE:
            glColor4f(1.0, 0.0, 1.0, alpha);
            break;
        case CYAN:
            glColor4f(0.0, 1.0, 1.0, alpha);
            break;
        default:
            glColor4f(0.7, 0.7, 0.7, alpha);
            break;
    }

    Point3d p1(-0.5, -0.5, 0.5);
    Point3d p2(0.5, -0.5, 0.5);
    Point3d p3(-0.5, 0.5, 0.5);
    Point3d p4(0.5, 0.5, 0.5);

    Point3d p5(-0.5, -0.5, -0.5);
    Point3d p6(0.5, -0.5, -0.5);
    Point3d p7(-0.5, 0.5, -0.5);
    Point3d p8(0.5, 0.5, -0.5);

    glBegin(GL_QUADS);

    // front
    glNormal3f(0.0, 0.0, 1.0);
    glVertex3f(p1.x(), p1.y(), p1.z());
    glVertex3f(p2.x(), p2.y(), p2.z());
    glVertex3f(p4.x(), p4.y(), p4.z());
    glVertex3f(p3.x(), p3.y(), p3.z());

    // right
    glNormal3f(1.0, 0.0, 0.0);
    glVertex3f(p4.x(), p4.y(), p4.z());
    glVertex3f(p2.x(), p2.y(), p2.z());
    glVertex3f(p6.x(), p6.y(), p6.z());
    glVertex3f(p8.x(), p8.y(), p8.z());

    // left
    glNormal3f(-1.0, 0.0, 0.0);
    glVertex3f(p1.x(), p1.y(), p1.z());
    glVertex3f(p3.x(), p3.y(), p3.z());
    glVertex3f(p7.x(), p7.y(), p7.z());
    glVertex3f(p5.x(), p5.y(), p5.z());

    // back
    glNormal3f(0.0, 0.0, -1.0);
    glVertex3f(p6.x(), p6.y(), p6.z());
    glVertex3f(p5.x(), p5.y(), p5.z());
    glVertex3f(p7.x(), p7.y(), p7.z());
    glVertex3f(p8.x(), p8.y(), p8.z());

    // top
    glNormal3f(0.0, 1.0, 0.0);
    glVertex3f(p3.x(), p3.y(), p3.z());
    glVertex3f(p4.x(), p4.y(), p4.z());
    glVertex3f(p8.x(), p8.y(), p8.z());
    glVertex3f(p7.x(), p7.y(), p7.z());

    // down
    glNormal3f(0.0, -1.0, 0.0);
    glVertex3f(p1.x(), p1.y(), p1.z());
    glVertex3f(p5.x(), p5.y(), p5.z());
    glVertex3f(p6.x(), p6.y(), p6.z());
    glVertex3f(p2.x(), p2.y(), p2.z());

    glEnd();

    if (ghost)
        glDisable(GL_BLEND);
}

void CCanvas::drawPiece(Piece piece, bool ghost) {
    const Point3f* positions = piece.getTransformedPositions();
    int size = piece.getSize();

    int i;
    for (i = 0; i < size; i++) {
        glPushMatrix();
        glTranslated(positions[i].y() - offset_x, positions[i].z(), positions[i].x() - offset_z);
        drawCube(piece.getColor(), ghost);
        glPopMatrix();
    }
}

void CCanvas::drawField() {

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, _textureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    float tex_x_offset = game.getY() / 2.0;
    float tex_z_offset = game.getX() / 2.0;
    float tex_y_offset = game.getZ() / 2.0;

    glColor3f(0.6, 0.6, 0.6);

    glBegin(GL_QUADS);

    // bottom
    glNormal3f(0.0, 1.0, 0.0);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-offset_x - 0.5, -offset_y, -offset_z - 0.5);
    glTexCoord2f(0.0f, tex_z_offset);
    glVertex3f(-offset_x - 0.5, -offset_y, offset_z + 0.5);
    glTexCoord2f(tex_x_offset, tex_z_offset);
    glVertex3f(offset_x + 0.5, -offset_y, offset_z + 0.5);
    glTexCoord2f(tex_x_offset, 0.0f);
    glVertex3f(offset_x + 0.5, -offset_y, -offset_z - 0.5);

    // left
    glNormal3f(1.0, 0.0, 0.0);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-offset_x - 0.5, -offset_y, offset_z + 0.5);
    glTexCoord2f(0.0f, tex_z_offset);
    glVertex3f(-offset_x - 0.5, -offset_y, -offset_z - 0.5);
    glTexCoord2f(tex_y_offset, tex_z_offset);
    glVertex3f(-offset_x - 0.5, -offset_y + game.getZ(), -offset_z - 0.5);
    glTexCoord2f(tex_y_offset, 0.0f);
    glVertex3f(-offset_x - 0.5, -offset_y + game.getZ(), offset_z + 0.5);

    // back
    glNormal3f(0.0, 0.0, 1.0);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(offset_x + 0.5, -offset_y, offset_z + 0.5);
    glTexCoord2f(0.0f, tex_x_offset);
    glVertex3f(-offset_x - 0.5, -offset_y, offset_z + 0.5);
    glTexCoord2f(tex_y_offset, tex_x_offset);
    glVertex3f(-offset_x - 0.5, -offset_y + game.getZ(), offset_z + 0.5);
    glTexCoord2f(tex_y_offset, 0.0f);
    glVertex3f(offset_x + 0.5, -offset_y + game.getZ(), offset_z + 0.5);

    // right
    glNormal3f(-1.0, 0.0, 0.0);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(offset_x + 0.5, -offset_y, -offset_z - 0.5);
    glTexCoord2f(0.0f, tex_z_offset);
    glVertex3f(offset_x + 0.5, -offset_y, offset_z + 0.5);
    glTexCoord2f(tex_y_offset, tex_z_offset);
    glVertex3f(offset_x + 0.5, -offset_y + game.getZ(), offset_z + 0.5);
    glTexCoord2f(tex_y_offset, 0.0f);
    glVertex3f(offset_x + 0.5, -offset_y + game.getZ(), -offset_z - 0.5);

    //front
    glNormal3f(0.0, 0.0, -1.0);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-offset_x - 0.5, -offset_y, -offset_z - 0.5);
    glTexCoord2f(0.0f, tex_x_offset);
    glVertex3f(offset_x + 0.5, -offset_y, -offset_z - 0.5);
    glTexCoord2f(tex_y_offset, tex_x_offset);
    glVertex3f(offset_x + 0.5, -offset_y + game.getZ(), -offset_z - 0.5);
    glTexCoord2f(tex_y_offset, 0.0f);
    glVertex3f(-offset_x - 0.5, -offset_y + game.getZ(), -offset_z - 0.5);

    glEnd();

    glDisable(GL_TEXTURE_2D);

    int color;

    for (int j = 0; j < game.getZ(); j++) {
        for (int i = 0; i < game.getY(); i++) {
            for (int k = 0; k < game.getX(); k++) {
                glPushMatrix();
                glTranslated((double) i - offset_x, (double) j, (double) k - offset_z);
                color = game.getValueAtPosition(k, i, j);
                if (color != 0) {
#ifdef DRAW_HEIGHT
                    drawCube((Colors) ((j % 6) + 1), false);
#else
                    drawCube((Colors) color, false);
#endif
                }
                glPopMatrix();
            }
        }
    }
}

void setCommands(int* w, int* a, int* s, int* d, int theta) {
    int value = (360 + (theta % 360)) % 360;
    if (value < 135 && value >= 45) {
        *w = Qt::Key_D;
        *a = Qt::Key_W;
        *s = Qt::Key_A;
        *d = Qt::Key_S;
    } else if (value < 225 && value >= 135) {
        *w = Qt::Key_S;
        *a = Qt::Key_D;
        *s = Qt::Key_W;
        *d = Qt::Key_A;
    } else if (value < 315 && value >= 225) {
        *w = Qt::Key_A;
        *a = Qt::Key_S;
        *s = Qt::Key_D;
        *d = Qt::Key_W;
    } else {
        *w = Qt::Key_W;
        *a = Qt::Key_A;
        *s = Qt::Key_S;
        *d = Qt::Key_D;
    }
}

void CCanvas::keyPressEvent(QKeyEvent *event) {
    command next;
    int w, a, s, d;
    setCommands(&w, &a, &s, &d, round(theta));

    if (event->KeyPress && event->key() == Qt::Key_Shift) {
        shift = -1;
    }

    if (event->KeyRelease && event->key() == Qt::Key_Shift) {
        shift = 1;
    }

    if (event->key() == w) {//Qt::Key_W) {
        next.a = X;
        next.o = NEGATIVE;
        next.op = MOVE;
        game.addCommand(next);
    }

    if (event->key() == s) {//Qt::Key_S) {
        next.a = X;
        next.o = POSITIVE;
        next.op = MOVE;
        game.addCommand(next);
    }

    if (event->key() == a) {//Qt::Key_A) {
        next.a = Y;
        next.o = NEGATIVE;
        next.op = MOVE;
        game.addCommand(next);
    }

    if (event->key() == d) {//Qt::Key_D) {
        next.a = Y;
        next.o = POSITIVE;
        next.op = MOVE;
        game.addCommand(next);
    }

    if (event->key() == Qt::Key_Space) {
        next.op = FALL;
        game.addCommand(next);
    }

    if (event->key() == Qt::Key_Q) {
        next.a = X;
        next.op = ROTATE;
        next.o = (Orientation) shift;
        game.addCommand(next);
    }

    if (event->key() == Qt::Key_E) {
        next.a = Y;
        next.op = ROTATE;
        next.o = (Orientation) shift;
        game.addCommand(next);
    }

    if (event->key() == Qt::Key_R) {
        next.a = Z;
        next.op = ROTATE;
        next.o = (Orientation) shift;
        game.addCommand(next);
    }

    if (event->key() == Qt::Key_P) {
        pause = !pause;
        _button->setEnabled(pause);
    }

    if (event->key() == Qt::Key_Up) {
        camera_y += 0.5;
    }
    if (event->key() == Qt::Key_Down) {
        camera_y -= 0.5;
    }
    if (event->key() == Qt::Key_Left) {
        theta += 10.0;
    }
    if (event->key() == Qt::Key_Right) {
        theta -= 10.0;
    }
    
    if(event->key() == Qt::Key_Return) {
        game.restartGame();
    }
    
    if (event->key() == 27) {
        exit(0);
    } else {
        QGLWidget::keyPressEvent(event);
    }
}

void CCanvas::showGameOver(){
	
	glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, _gameovertextureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	
	beta += 0.5;
	
	glPushMatrix();
	glRotated(beta, 0.0, 1.0, 0.0);
	glScaled(5.0, 5.0, 5.0);
	glTranslated(0.0, 1.0, 0.0);
	
	
	glBegin(GL_QUADS);
	
	glColor3f(1.0, 1.0, 1.0);
	glNormal3f(0.0, 0.0, 1.0);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-0.5, -0.5, 0.0);
	glTexCoord2f(0.0f, 1.0);
	glVertex3f(-0.5, 0.5, 0.0);
	glTexCoord2f(1.0f, 1.0);
	glVertex3f(0.5, 0.5, 0.0);
	glTexCoord2f(1.0f, 0.0);
	glVertex3f(0.5, -0.5, 0.0);
	
	glEnd();
	
	glPopMatrix();
	
	glDisable(GL_TEXTURE_2D);
}

//-----------------------------------------------------------------------------

void CCanvas::paintGL() {


    double tmp = mytime.getElapsedTime();
    if (tmp > elapsed && !game.getGameOver() && !pause) {
        elapsed = tmp + time_step;

        game.updateAll();
        changeScore(game.getScore());

        tau = 0.0;
    }

    Piece p = *game.getCurrentPiece();

    Piece g = game.getCurrentPieceCopy();

    game.getCollisionPosition(&g);

    //cout << p.comparePositions(g.getTransformedPositions(), g.getSize()) << endl;

    if (!pause && !game.getGameOver() && !p.comparePositions(g.getTransformedPositions(), g.getSize())){
        tau += 0.01;
	}
		

    //	cout << p.getTransformedPositions() << endl;
    //	cout << g.getTransformedPositions() << endl;



    // clear screen and depth buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // set model-view matrix
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    double rad = theta / (180 / PI);
    gluLookAt(sin(rad) * camera_dist, camera_y, cos(rad) * camera_dist, 0, game.getY() / 2, 0, 0, 1, 0); // camera position , "look at" point , view-up vector


    // set up a light source
    GLfloat diffuse [] = {1.0, 1.0, 1.0, 1.0};

    //    //GLfloat position[] = {-5.0, 5.0, 5.0, 1.0};
    //    GLfloat position[] = { sin(rad) * camera_dist, camera_y, cos(rad) * camera_dist};//sin(rad)*6,camera_y,cos(rad)*6, 1.0 };
    //    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    //    glLightfv(GL_LIGHT0, GL_POSITION, position);


    GLfloat position1[] = {5.0, 5.0, -5.0, 1.0};
    glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT1, GL_POSITION, position1);

    GLfloat position2[] = {-5.0, 5.0, 5.0, 1.0};
    glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT2, GL_POSITION, position2);

    //	GLfloat position3[] = {-5.0, 5.0, -5.0, 1.0};
    //    glLightfv(GL_LIGHT3, GL_DIFFUSE, diffuse);
    //    glLightfv(GL_LIGHT3, GL_POSITION, position3);
    //	
    //	GLfloat position4[] = {5.0, 5.0, 5.0, 1.0};
    //    glLightfv(GL_LIGHT4, GL_DIFFUSE, diffuse);
    //    glLightfv(GL_LIGHT4, GL_POSITION, position4);

    //Add ambient light
    GLfloat ambientColor[] = {0.2f, 0.2f, 0.2f, 1.0f}; //Color(0.2, 0.2, 0.2)
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);

    // turn on light
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_NORMALIZE);
    glEnable(GL_LIGHTING);
    //glEnable(GL_LIGHT0);
    glEnable(GL_LIGHT1);
    glEnable(GL_LIGHT2);
    glEnable(GL_LIGHT3);
    glEnable(GL_LIGHT4);

    // choose between flat and Gouraud shading
    //glShadeModel(GL_FLAT);
    glShadeModel(GL_SMOOTH);


    // set material coefficients
    GLfloat mat_emission[] = {0.0, 0.0, 0.0, 0.0};
    //GLfloat mat_ambient [] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat mat_diffuse [] = {0.8, 0.2, 0.2, 1.0};
    GLfloat mat_specular[] = {0.5, 0.5, 0.5, 1.0};

    glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
    //glMaterialfv( GL_FRONT, GL_AMBIENT,  mat_ambient  );
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialf(GL_FRONT, GL_SHININESS, 40.0);


	//showGameOver();
	
	if (game.getGameOver()) {
		game.fillField();
		//pause = true;
		showGameOver();
	}
	
	
	
	
	if (!game.getGameOver()) {
    glEnable(GL_CULL_FACE);
	
	

    // draw the game field (walls, floor and already fallen tetrominoes)
    drawField();



    glPushMatrix();

    glTranslated(0, -tau, 0);

    //glTranslated(, , ) // TODO find the position of the falling piece
    drawPiece(p, false);

    glPopMatrix();

    drawPiece(g, true);


    //	//display next piece
    //	glPushMatrix();
    //	
    //	//	glRotated(90, 1, 0, 0);
    //	//glRotated(-theta, 0, 1, 0);
    //	//	glTranslated(asin(rad), 0, acos(rad));
    //	//glTranslated(sin(rad) * 4, camera_y-game.getZ(), cos(rad) * 4);
    //	glTranslated(0, -game.getZ() / 2.0, 5);
    //	Piece n = *game.getNextPiece();
    //	drawPiece(n, false);
    //	
    //	glPopMatrix();



    glDisable(GL_CULL_FACE);

	}

}